﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyControlLibrary
{

    // PersistChildren 定义在 class 级别，表示其中的属性在转换为标记时的处理方式
    //  如果为 true ，表示属性将转化为标记的子元素处理，否则作为标记的属性处理
    // ParseChildren 定义在 class 级别，表示如何处理标记的子元素的内容。
    //  如果为 true，表示直接解析，并作为对象的属性，如果为 false，则表示将作为子控件进行处理，
    [System.Web.UI.PersistChildren(false)]
    [System.Web.UI.ParseChildren(true)]
    // DefaultProperty 定义在类级, 用来指定默认属性
    [System.ComponentModel.DefaultProperty("Text")]
    //
    // 图标文件必须为 16*16 的 Bitmap 格式，并且必须设置为嵌入的资源, Build Action 中设置为：Embedded Resource
    // 
    // 下面设置没有成功，将图片名称设置为与类同名成功
    // [System.Drawing.ToolboxBitmap(typeof(MyControlLabel), "MyControlLibrary.Bitmap1.bmp")]
    //
    // 指定控件的设计器
    //
    [System.ComponentModel.Designer( typeof( MyControlLabelDesigner))]
    public class MyControlLabel : System.Web.UI.WebControls.WebControl
    {
        // 构造函数，负责创建复杂的数据成员
        public MyControlLabel()
        {
            this.Location = new Location();
        }
        private string _text;

        // 默认情况下，类中定义的公共属性将作为控件的属性标记写入模版中，如下所示，此为默认设置
        //
        // <cc1:MyControlLabel ID="MyControlLabel1" runat="server" Text="Hello, world.">
        // </cc1:MyControlLabel>
        //
        [System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute)]
        public string Text
        {
            set { this._text = value; }
            get { return this._text; }
        }

        private string _message;

        // 通过 InnerDefaultProperty 设置，保存的内容将在标记之间，如下所示
        //
        // <cc1:MyControlLabel ID="MyControlLabel1" runat="server" Text="Hello, world.">
        //    Hello, 北京
        // </cc1:MyControlLabel>
        //
        [System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode .InnerDefaultProperty)]
        public string Message
        {
            set { this._message = value; }
            get { return this._message; }
        }

        // InnerProperty ，表示持久化的内容将作为一个子标记，元素的名字将作为子元素的名字
        //
        // <cc1:MyControlLabel ID="MyControlLabel1" runat="server" >
        //    <EmptyText>Hello</EmptyText>
        // </cc1:MyControlLabel>
        //
        private string _emptyText;
        [System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.InnerProperty)]
        public string EmptyText
        {
            set { this._emptyText = value; }
            get { return this._emptyText; }
        }

        // EncodedInnerDefaultProperty 表示当将内容保存在标记的文本内容中时，需要进行 HTML 的编码和解码
        // 下面为 HtmlText 保存的内容为 <a href="#">Click</a>
        //
        // <cc1:MyControlLabel ID="MyControlLabel1" runat="server">
        //    &lt;a href=&quot;#&quot;&gt;Click&lt;/a&gt;
        // </cc1:MyControlLabel>
        //
        private string _htmlText;
        [System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.EncodedInnerDefaultProperty)]
        public string HtmlText
        {
            set { this._htmlText = value; }
            get { return this._htmlText; }
        }

        private Location _loc;
        //
        // 通过设置 PersistenceMode 为 InnerProperty ，可以将此属性的内容以子元素的方式保存
        // DesignerSerializationVisibility 控制设计器序列化的可见性，不设置，在设计器中将为灰色
        //
        [System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode .InnerProperty)]
        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)]
        public Location Location
        {
            set { this._loc = value; }
            get { return this._loc; }
        }

        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            writer.Write("<h1>Location Country: {0}, City: {1}", this.Location.Country, this.Location.City);
        }

        protected override object SaveViewState()
        {
            object x = base.SaveViewState();

            object y = this.Location.SaveViewState();

            return new System.Web.UI.Pair(x, y);
        }

        protected override void LoadViewState(object savedState)
        {
            if (savedState != null)
            {
                System.Web.UI.Pair pair = savedState as System.Web.UI.Pair;

                base.LoadViewState(pair.First);
                this.Location.LoadViewState(pair.Second);
            }
        }

    }

    /// <summary>
    /// Location 对象
    /// System.ComponentModel.TypeConverter 标签指定 Location 与标记之间的转换对象
    /// System.ComponentModel.Editor 标签指定 Location 使用的编辑器对象
    /// </summary>
    [System.Serializable]
    [System.ComponentModel.TypeConverter( typeof( LocationTypeConverter))]
    [System.ComponentModel.Editor( typeof( LocationEditor), typeof( System.Drawing.Design.UITypeEditor))]
    public class Location : System.Web.UI.IStateManager
    {
        private string _country;
        public string Country
        {
            set { this._country = value; }
            get { return this._country; }
        }

        private string _city;
        public string City
        {
            set { this._city = value; }
            get { return this._city; }
        }

        // 通过静态构造函数来创建一个唯一的对象
        public readonly static Location Empty;

        static Location()
        {
            Empty = new Location();
        }

        public Location() { }
        public Location(string country, string city)
        {
            this._country = country;
            this._city = city;
        }

        public override string ToString()
        {
            return string.Format("{0}, {1}", this._country, this._city);
        }

        #region IStateManager Members

        private bool _isTrackingViewState = false;

        public bool IsTrackingViewState
        {
            get { return this._isTrackingViewState; }
        }

        public void TrackViewState()
        {
            this._isTrackingViewState = true;
        }

        public void LoadViewState(object state)
        {
            if (state != null)
            {
                if (state is System.Web.UI.Pair)
                {
                    System.Web.UI.Pair pair = state as System.Web.UI.Pair;
                    this._city = pair.First as string;
                    this._country = pair.Second as string;
                }
            }
        }

        public object SaveViewState()
        {
            return new System.Web.UI.Pair(this._city, this._country);
        }        

        #endregion
    }

    /// <summary>
    /// Location 的类型转换器
    /// 
    /// 至少应用重载四个方法
    /// 
    /// CanConvertFrom
    /// CanConvertTo
    /// 
    /// ConvertFrom
    /// ConvertTo
    /// 
    /// </summary>
    public class LocationTypeConverter : System.ComponentModel.TypeConverter
    {
        /// <summary>
        /// 从某种类型转换为指定的对象
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="sourceType">原始类型</param>
        /// <returns></returns>
        public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
        {
            // 如果是字符串可以进行转换
            // 其他情况通过基类默认处理
            if (sourceType == typeof(string))
            {
                return true;
            }
            else
            {
                return base.CanConvertFrom(context, sourceType);
            }
        }

        /// <summary>
        /// 从字符串转换回对象
        /// </summary>
        /// <param name="context"></param>
        /// <param name="culture"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override object ConvertFrom(
            System.ComponentModel.ITypeDescriptorContext context,
            System.Globalization.CultureInfo culture,
            object value)
        {
            // 如果提供的 value 不是字符串类型，由基类处理
            if (!(value is string))
            {
                return base.ConvertFrom(context, culture, value);
            }

            // 首先将字符串的首尾空格删除
            string str = ((string)value).Trim();

            if (str.Length == 0)
            {
                return Location.Empty;
            }

            // 处理内容
            char[] splitchar = { ',' };
            string[] values = ((string)value).Split(splitchar);
            // 如果不是正好两部分组成，认为有问题
            if (values.Length != 2)
                return Location.Empty;

            // 第一部分为国家，第二部分为城市
            string country = values[0];
            string city = values[1];
            return new Location(country, city);
        }

        /// <summary>
        /// 用来判断是否可以将对象转换为指定的类型
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="destinationType">目标类型</param>
        /// <returns></returns>
        public override bool CanConvertTo(
            System.ComponentModel.ITypeDescriptorContext context, 
            Type destinationType)
        {
            // 可以转换到字符串
            if (destinationType == typeof(string) ||
                destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor))
                return true;
            else
                return base.CanConvertTo(context, destinationType);
        }
        /// <summary>
        ///  将对象转换为字符串
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="culture">文化</param>
        /// <param name="value">原始对象</param>
        /// <param name="destinationType">目标类型</param>
        /// <returns></returns>
        public override object ConvertTo(
            System.ComponentModel.ITypeDescriptorContext context,
            System.Globalization.CultureInfo culture,
            object value,
            Type destinationType)
        {
            if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor))
            {
                Location location = value as Location;
                Type stringType = typeof(string);
                // 取得两个字符串参数的构造函数
                System.Reflection.MemberInfo mi = typeof(Location).GetConstructor(
                    new Type[] { stringType, stringType });
                // 准备构造函数的参数
                object[] args = new object[] { location.Country, location.City };
                object obj = new System.ComponentModel.Design.Serialization.InstanceDescriptor(mi, args);
                return obj;
            }

            if (destinationType != typeof(string))
            {
                return base.ConvertTo(context, culture, value, destinationType);
            }

            if (value != null)
            {
                Location location = value as Location;
                if (location != Location.Empty)
                {
                    return location.ToString();
                }
            }

            return string.Empty;

        }
    }

    // 定义显示效果的设计器
    // System.Web.UI.Design 定义在程序集 System.Design 中
    //
    public class MyControlLabelDesigner : System.Web.UI.Design.ControlDesigner
    {
        public override string GetDesignTimeHtml()
        {
            // 取得当前控件的引用
            System.Web.UI.Control control = this.Component as System.Web.UI.Control;
            string control_id = control.ID;     // 取得控件的 ID
            System.Text.StringBuilder builder = new StringBuilder();
            builder.AppendFormat(
                "<label id='{0}' style='align:center;font-size:small;font-weight:bold;color:@00008b;valign:middle;background-color:#f0f0f0;border-width:2px;border-style:outset;height:20px;width:20px;'><center>{0}</center></label>", control_id);
            return builder.ToString();

        }
    }

    // 定义 Location 的编辑器
    //
    //
    public class LocationEditor : System.Drawing.Design.UITypeEditor
    {
        public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(
            System.ComponentModel.ITypeDescriptorContext context)
        {
            // 使用模式对话框进行处理
            return System.Drawing.Design.UITypeEditorEditStyle.Modal;
        }

        public override object EditValue(
            System.ComponentModel.ITypeDescriptorContext context, 
            IServiceProvider provider, object value)
        {
            System.Windows.Forms.Design.IWindowsFormsEditorService wfe
                = provider.GetService( typeof( System.Windows.Forms.Design.IWindowsFormsEditorService))
                    as System.Windows.Forms.Design.IWindowsFormsEditorService;
            if (wfe != null)
            {
                Location location = value as Location;
                MyLocationEditorForm form = new MyLocationEditorForm(location.Country, location.City);
                using (form)
                {
                    wfe.ShowDialog(form);
                    return new Location(form.Country, form.City);
                }
            }
            

            return base.EditValue(context, provider, value);
        }
    }

    /// <summary>
    /// Location 在设计器中的编辑器，使用 WinForm 进行定义
    /// </summary>
    public class MyLocationEditorForm : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox tbCountry;
        private System.Windows.Forms.TextBox tbCity;
        private System.Windows.Forms.Button btnOk;

        private System.ComponentModel.Container components = null;

        public string Country
        {
            get { return this.tbCountry.Text; }
        }

        public string City
        {
            get { return this.tbCity.Text; }
        }

        // 构造函数
        public MyLocationEditorForm()
        {
            InitializeComponent();
        }

        public MyLocationEditorForm(string country, string city)
            : this()
        {
            this.tbCountry.Text = country;
            this.tbCity.Text = city;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        // 设计器的代码
        private void InitializeComponent()
        {

            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.tbCity = new System.Windows.Forms.TextBox();
            this.tbCountry = new System.Windows.Forms.TextBox();
            this.btnOk = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(24, 24);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(47, 12);
            this.label1.Text = "Country";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(42, 68);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(29, 12);
            this.label2.Text = "City";
            // 
            // tbCity
            // 
            this.tbCity.Location = new System.Drawing.Point(80, 65);
            this.tbCity.Name = "tbCity";
            this.tbCity.Size = new System.Drawing.Size(200, 21);
            this.tbCity.TabIndex = 1;
            // 
            // tbCountry
            // 
            this.tbCountry.Location = new System.Drawing.Point(80, 21);
            this.tbCountry.Name = "tbCountry";
            this.tbCountry.Size = new System.Drawing.Size(200, 21);
            this.tbCountry.TabIndex = 0;
            // 
            // btnOk
            // 
            this.btnOk.Location = new System.Drawing.Point(205, 103);
            this.btnOk.Name = "btnOk";
            this.btnOk.Size = new System.Drawing.Size(75, 23);
            this.btnOk.TabIndex = 2;
            this.btnOk.Text = "Ok";
            this.btnOk.UseVisualStyleBackColor = true;
            this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK;
            this.btnOk.Click +=new EventHandler(BtnOk_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(312, 146);
            this.Controls.Add(this.btnOk);
            this.Controls.Add(this.tbCountry);
            this.Controls.Add(this.tbCity);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Location Editor";
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.Load +=new EventHandler(MyLocationEditorForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        void MyLocationEditorForm_Load(object sender, EventArgs e)
        {
            
        }

        void BtnOk_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
